fix(perf): scope runPostNativeCha to changed files on incremental builds#1490
Conversation
… diff Adds snapshot-pre-bash.sh (PreToolUse Bash) + track-bash-writes.sh (PostToolUse Bash): the pre-hook captures git status --porcelain to a per-worktree temp file before each Bash call; the post-hook diffs the before/after state and appends newly modified or created files to .claude/session-edits.log. This closes the gap where files written by sed -i, printf redirects, tee, heredocs, or build tools (Cargo.lock, lockfiles) were never recorded, causing guard-git.sh to emit false-positive BLOCKED errors. Closes #1457
- clojure.rs: annotate lifetime-anchor assignment to silence false-positive - cfg.rs: remove never-called start_line_of method - complexity.rs: remove never-constructed NotHandled variant; convert irrefutable if-let patterns to plain let destructures - dataflow.rs: remove never-read callee fields from CallReturn/Destructured - incremental.rs: remove never-read lang field from CacheEntry cargo check and cargo clippy both clean after these changes.
Adds .github/workflows/perf-canary.yml — a path-filtered workflow that fires on PRs touching src/extractors/, src/domain/graph/, or crates/** and runs only the incremental-benchmark suite (full build + no-op + 1-file rebuild, both engines). Catches the class of regressions that accumulated invisibly across the Phase 8.x PRs and were only detected at v3.12.0 publish time. The regression guard gains BENCH_CANARY=1 mode: raises thresholds to 50%/100%/150% (standard/noisy/WASM) and skips the build, query, and resolution suites — only incremental checks run. This absorbs shared- runner timing variance while still blocking catastrophic regressions (+98% full build, +1827% 1-file rebuild from v3.12.0). Closes #1433
On incremental builds, runPostNativeCha previously scanned all call→qualified-method edges in the DB (~12ms flat, O(graph size)), even for 1-file changes where no hierarchy or RTA evidence changed. Add two cheap indexed gate queries. Gate A checks whether any changed file introduced a class/interface/trait/struct/record node (hierarchy may have new implementors reachable from unchanged call sites). Gate B checks whether any changed file added a call edge to a class-kind target (RTA set may have grown, enabling previously filtered expansions in unchanged callers). If neither gate fires, restrict the candidate query to src.file IN changedFiles — safe because the hierarchy and instantiated set are unchanged for all other files. Full builds (isFullBuild=true) and cases where either gate fires retain the existing full-scan behaviour. Mirrors the changed-files scoping pattern of runPostNativeThisDispatch. Closes #1441
|
Claude finished @carlos-alm's task —— View job PR Review
|
Greptile SummaryThis PR scopes
Confidence Score: 5/5Safe to merge — the gate logic is conservative and the scoped path is only taken when the full-graph state is provably unchanged outside the changed files. Both gates correctly fall back to the full scan on any ambiguity. The previously-identified Gate B schema gap (missing constructor/function kinds) is already fixed in this revision. The global implementors map and RTA set are always built from the full DB regardless of scoping. The chunked IN-list pattern mirrors the existing battle-tested approach in runPostNativeThisDispatch. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[runPostNativeCha called] --> B{changedFiles param}
B -- null / isFullBuild --> C[Full scan: all call to method edges]
B -- string array --> D{changedFiles.length > 0?}
D -- No / empty --> C
D -- Yes --> E[Gate A: nodes WHERE file IN changedFiles AND kind IN class/interface/trait/struct/record]
E -- row found --> F[gateAFired = true, full scan]
E -- no row --> G[Gate B: edges WHERE kind=calls AND tgt.kind IN class/constructor/function AND src.file IN changedFiles]
G -- row found --> H[gateBFired = true, full scan]
G -- no row --> I[scopeToChangedFiles = true]
F --> C
H --> C
I --> K[Scoped scan: callToMethods WHERE src.file IN changedFiles, chunked 500-row batches]
C --> L[BFS expand implementors using global implementors map plus instantiated RTA set]
K --> L
L --> M[Insert new CHA edges via batchInsertEdges]
Reviews (9): Last reviewed commit: "fix: resolve merge conflicts with main" | Re-trigger Greptile |
Codegraph Impact Analysis2 functions changed → 6 callers affected across 6 files
|
…back schema
Gate B previously checked only `tgt.kind = 'class'`, but the RTA seed has a
fallback that matches `tgt.kind IN ('constructor', 'function')` when no
class-kind constructor edges exist (older native engine schemas). On codebases
where the fallback path is always active, Gate B would never fire, causing
scopeToChangedFiles to be set incorrectly and silently dropping CHA edges for
unchanged callers whose RTA evidence lives in the fallback-schema rows.
Broaden Gate B to `tgt.kind IN ('class', 'constructor', 'function')` to mirror
the full two-shape RTA seed. Also fix formatter violation on the .all() cast.
|
Addressed all three concerns from the Claude review:
|
docs check acknowledged — merge resolution only; no new features or API changes to document

runPostNativeChascanned all call→qualified-method edges on every native build including 1-file incrementals (~12ms flat, grows with graph size).Adds two cheap indexed gate queries: if no class hierarchy changes and no new RTA evidence landed in the changed files, scopes the candidate scan to
src.file IN changedFiles. Falls back to the full scan when either gate fires (hierarchy or RTA set may have changed for callers in other files).Gate A: any
class/interface/trait/struct/recordnodes in changed files → hierarchy may have new implementors reachable from unchanged call sites → full scan.Gate B: any
callsedges from changed-file sources toclass-kind targets → RTA set may have grown, enabling previously filtered expansions in unchanged callers → full scan.If neither gate fires, the hierarchy and instantiated set are provably unchanged outside the changed files, so restricting candidates to
src.file IN changedFilesis safe.Full builds (
isFullBuild=true) and gate-fired paths retain the existing full-scan behaviour unchanged.Mirrors the changed-files scoping pattern of
runPostNativeThisDispatch.Closes #1441